<?php

/**
 * Element de parsing pour les tags de la forme {%...} ou {%%...}
 *
 * @category   McCube
 * @package    Template\Parser\Element
 * @author     Leiha Sellier <leiha.sellier@gmail.com>
 * @license    http://creativecommons.org/licenses/by/3.0/  CC BY 3.0
 * @link       http://code.google.com/p/mechanics-cubes/
 */
class McCube_Template_Parser_Element_Command
    extends McCube_Template_Parser_Abstract{

    protected static $_balancedTags = array(
        'if', 'foreach'
    );

     protected static $_inlineTags = array(
        'action'
    );

    protected $type = '%';
    protected $typeOnString = 'Command';

    /**
     * Parses a string to find the items to command
     *
     * @param string $string
     * @return string
     */
    public function parse($string){
        foreach(self::$_balancedTags as $tag){
            $string = $this->_parseBalancedTag($tag, $string);
        }
        foreach(self::$_inlineTags as $tag){
            $string = $this->_parseTag($tag, $string);
        }
        return $string;
    }

    /** */
    protected function _parseCallback($mode, $type, $sType, $tag, $inTag='', $content=''){

        $inTag = parent::_stripSlashes($inTag);

        $objName     = 'McCube_Template_Parser_Element_'.$this->typeOnString.'_'.ucfirst($tag);
        $objInstance = new $objName($this);
        return $objInstance->{$mode.$sType}(
              $inTag
            , $this->parser->parseString($content)
        );
    }

    /** */
    protected function _preg($pattern, $string){
        if(preg_match_all(
              $pattern
            , $string
            , $matches
            , PREG_OFFSET_CAPTURE
        ))
            return $matches;
    }

    /** */
    protected function _getPattern($tag){
        return '~(?<!\\\)'
            .McCube_Template_Parser::$delimiters[0]
                .'(['.$this->type.']{0,1})'
                .'((['.$this->type.'/])('.$tag.'))'
                .'(.*?)'
            .'(?<!\\\)'.McCube_Template_Parser::$delimiters[1]
            .'~'
        ;
    }

    /** */
    protected function _parseTag($tag, $string){
        if(!$matches = $this->_preg(
              $this->_getPattern($tag)
            , $string
        ))
            return $string;

        foreach($matches[2] as $key=>$match){
            $begin = $matches[0][$key][1];
            $mode  = $matches[1][$key][0] ? 'execution' : 'build';
            $forReplace  [] = substr($string, $begin, $matches[0][$key][1] + strlen($matches[0][$key][0]) - $begin);
            $substituting[] = $this->_parseCallback(
                  $mode
                , $this->type
                , ($mode == 'build' ? ($matches[3][$key][0] == '/' ? 'End' : 'Start') : '')
                , $matches[4][$key][0]
                , trim($matches[5][$key][0])
                , ''
            );
        }

        if(isset($forReplace))
            return str_replace($forReplace, $substituting, $string);

        return $string;
    }

    /** */
    protected function _parseBalancedTag($tag, $string){
        if(!$matches = $this->_preg(
              $this->_getPattern($tag)
            , $string
        ))
            return $string;

        $return  = array();
        $counter = array(0,0);
        foreach($matches[2] as $key=>$match){
            if(!$counter[0] && $match[0] == $this->type.$tag){
                $begin        = $matches[0][$key][1];
                $mode         = $matches[1][$key][0] ? 'execution' : 'build';
                $beginContent = $matches[0][$key][1] + strlen($matches[0][$key][0]);
                $beginInTag   = $matches[5][$key][0];
                $tag_ = $matches[4][$key][0];
                $counter[0]++;
                continue;
            } elseif($counter[0] && $match[0] == $this->type.$tag){
                $counter[0]++;
                continue;
            } elseif($counter[0] && $match[0] == '/'.$tag){
                $counter[1]++;
                if($counter[1] == $counter[0]){
                    $counter        = array(0,0);
                    $forReplace  [] = substr($string, $begin, $matches[0][$key][1] + strlen($matches[0][$key][0]) - $begin);
                    $substituting[] = $this->_parseCallback(
                          $mode
                        , $this->type
                        , ''
                        , $tag_
                        , trim($beginInTag)
                        , substr($string, $beginContent, $matches[0][$key][1]-$beginContent)
                    );
                }
            }
        }

        if(isset($forReplace))
            return str_replace($forReplace, $substituting, $string);

        return $string;
    }

}